﻿using Devil;
using UnityEngine;
using UnityEngine.Serialization;

namespace GameToolkit.BehaviourTree
{
    [System.Serializable]
    [CompositeCategory]
    [CompositeTitle("计时器")]
    [CompositeIcon("55abd877c2d24084880302966bad8ac3")]
    public class TimerDecoration : Composite
    {
        [SerializeField, Min(0), FormerlySerializedAs("m_Seconds")]
        float m_MinSeconds = 1f;

        [SerializeField, Min(0)]
        float m_MaxSeconds = 1f;

        [SerializeField]
        bool m_TimeoutSuccess = true;

        public override string GetDescription()
        {
            var res = m_TimeoutSuccess ? NodeState.Success : NodeState.Failed;
            var min = Mathf.Max(0, m_MinSeconds);
            var max = Mathf.Max(0, m_MaxSeconds);
            if (min < max)
                return $"计时: {min}s ~ {max}s 后 {res}";
            else
                return $"计时: {min}s 后 {res}";
        }

        public override BehaviourTreeObject Create()
        {
            return new Impl();
        }

        class Impl : Decoration, IExecutableEntryCallback, IInterruptionSource
        {
            long mTimeStamp;
            TimerDecoration mCfg;
            long mDuration;

            protected override void OnCreate(BehaviourTreePlayer player)
            {
                mCfg = composite as TimerDecoration;
            }
            protected override void OnDestroy(BehaviourTreePlayer player)
            {
            }

            public void OnStart(BehaviourTreePlayer player)
            {
                mTimeStamp = System.DateTime.UtcNow.Ticks;
                float sec;
                if (mCfg.m_MinSeconds < mCfg.m_MaxSeconds)
                    sec = ParallelUnity.GetRandomValue(mCfg.m_MinSeconds, mCfg.m_MaxSeconds);
                else
                    sec = mCfg.m_MinSeconds;
                mDuration = (long)((double)sec * ParallelDispatcher.second_2_ticks);
            }

            public void OnStop(BehaviourTreePlayer player)
            {
            }

            NodeState IInterruptionSource.GetInterruptResult(BehaviourTreePlayer player)
            {
#if UNITY_EDITOR
                if (mCfg.m_MinSeconds >= mCfg.m_MaxSeconds)
                    mDuration = (long)((double)mCfg.m_MinSeconds * ParallelDispatcher.second_2_ticks);
                else if(mDuration < mCfg.m_MinSeconds || mDuration > mCfg.m_MaxSeconds)
                    mDuration = (long)((double)ParallelUnity.GetRandomValue(mCfg.m_MinSeconds, mCfg.m_MaxSeconds) * ParallelDispatcher.second_2_ticks);
#endif
                if (AttachedNode.State == NodeState.Running && System.DateTime.UtcNow.Ticks - mTimeStamp >= mDuration)
                    return mCfg.m_TimeoutSuccess ? NodeState.Success : NodeState.Failed;
                else
                    return NodeState.Running;
            }

            NodeState IInterruptionSource.GetNotInterruptResult(BehaviourTreePlayer player)
            {
#if UNITY_EDITOR
                if (mCfg.m_MinSeconds >= mCfg.m_MaxSeconds)
                    mDuration = (long)((double)mCfg.m_MinSeconds * ParallelDispatcher.second_2_ticks);
                else if (mDuration < mCfg.m_MinSeconds || mDuration > mCfg.m_MaxSeconds)
                    mDuration = (long)((double)ParallelUnity.GetRandomValue(mCfg.m_MinSeconds, mCfg.m_MaxSeconds) * ParallelDispatcher.second_2_ticks);
#endif
                if (AttachedNode.State == NodeState.Running && System.DateTime.UtcNow.Ticks - mTimeStamp >= mDuration)
                    return mCfg.m_TimeoutSuccess ? NodeState.Failed : NodeState.Success;
                else
                    return NodeState.Running;
            }
        }
    }
}
